package p16_切点匹配;

import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

/**
 * spring中如何匹配@Transactional注解？AspectJExpressionPointcut的匹配方法只能匹配方法
 * 所以需要重写匹配方法，能让我们以自定义方式进行匹配
 * spring提供的工具类，MergedAnnotations.from判断类或方法上是否有指定注解
 */
public class _2_TransactionalMatches {

    public static void main(String[] args) throws NoSuchMethodException {
        //StaticMethodMatcherPointcut是一个Pointcut的抽象实现类，我们需要自定义匹配规则
        StaticMethodMatcherPointcut pt = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                //自定义匹配规则
                //1.检查方法有无注解
                MergedAnnotations annotations = MergedAnnotations.from(method);
                if (annotations.isPresent(Transactional.class)) {
                    System.out.println("方法匹配成功");
                    return true;
                }
                //2.检查类上有无注解
                annotations = MergedAnnotations.from(targetClass);
                if (annotations.isPresent(Transactional.class)) {
                    System.out.println("类匹配成功");
                    return true;
                }
                //3.判断父类或者接口有无注解
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                if (annotations.isPresent(Transactional.class)) {
                    System.out.println("接口匹配成功");
                    return true;
                }
                return false;
            }
        };

        //匹配方法经过我们的重写，就可以匹配类上的注解或者接口上的注解了
        System.out.println(pt.matches(Target1.class.getMethod("foo"), Target1.class));
        System.out.println(pt.matches(Target1.class.getMethod("bar"), Target1.class));
        System.out.println(pt.matches(Target2.class.getMethod("bar"), Target2.class));
    }

    @Transactional
    interface T1 {}

    @Transactional
    static class Target1  {
        @Transactional
        public void foo() {}
        public void bar() {}
    }

    static class Target2 implements T1  {
        public void bar() {}
    }
}
